SIGSEGV优化版代码

时间:2011-11-28 22:19:10

标签: c gcc assembly segmentation-fault sigsegv

我对英特尔指令集的了解有点生疏。你能告诉我为什么我可能在我的函数的优化版本中遇到分段错误(如果你能告诉我为什么我不能在代码的-O0版本中得到它的话,可以获得奖励积分。

这是由GCC 4.1.2编译的C代码。

这是崩溃时GDB的“disas”命令的结果:

   0x00000000004263e5 <+0>:     sub    $0x8,%rsp
   0x00000000004263e9 <+4>:     movsd  %xmm2,(%rsp)
   0x00000000004263ee <+9>:     divsd  %xmm1,%xmm0
   0x00000000004263f2 <+13>:    callq  0x60f098 <log@plt>
=> 0x00000000004263f7 <+18>:    andpd  0x169529(%rip),%xmm0        
   0x00000000004263ff <+26>:    movsd  (%rsp),%xmm1
   0x0000000000426404 <+31>:    ucomisd %xmm0,%xmm1
   0x0000000000426408 <+35>:    seta   %al
   0x000000000042640b <+38>:    movzbl %al,%eax
   0x000000000042640e <+41>:    add    $0x8,%rsp
   0x0000000000426412 <+45>:    retq   

这是函数的原始来源:

char is_within_range(double a, double b, double range) {
  double ratio = a / b;
  double logRatio = fabs(log(ratio));
  return logRatio < range;
}

这里的参考是代码的非优化版本:

   0x00000000004263e5 <+0>: push   %rbp
   0x00000000004263e6 <+1>: mov    %rsp,%rbp
   0x00000000004263e9 <+4>: sub    $0x30,%rsp
   0x00000000004263ed <+8>: movsd  %xmm0,-0x18(%rbp)
   0x00000000004263f2 <+13>:    movsd  %xmm1,-0x20(%rbp)
   0x00000000004263f7 <+18>:    movsd  %xmm2,-0x28(%rbp)
   0x00000000004263fc <+23>:    movsd  -0x18(%rbp),%xmm0
   0x0000000000426401 <+28>:    divsd  -0x20(%rbp),%xmm0
   0x0000000000426406 <+33>:    movsd  %xmm0,-0x10(%rbp)
   0x000000000042640b <+38>:    mov    -0x10(%rbp),%rax
   0x000000000042640f <+42>:    mov    %rax,-0x30(%rbp)
   0x0000000000426413 <+46>:    movsd  -0x30(%rbp),%xmm0
   0x0000000000426418 <+51>:    callq  0x610608 <log@plt>
   0x000000000042641d <+56>:    movapd %xmm0,%xmm1
   0x0000000000426421 <+60>:    movsd  0x16b6b7(%rip),%xmm0
   0x0000000000426429 <+68>:    andpd  %xmm1,%xmm0
   0x000000000042642d <+72>:    movsd  %xmm0,-0x8(%rbp)
   0x0000000000426432 <+77>:    movsd  -0x8(%rbp),%xmm1
   0x0000000000426437 <+82>:    movsd  -0x28(%rbp),%xmm0
   0x000000000042643c <+87>:    ucomisd %xmm1,%xmm0
   0x0000000000426440 <+91>:    seta   %al
   0x0000000000426443 <+94>:    movzbl %al,%eax
   0x0000000000426446 <+97>:    leaveq 
   0x0000000000426447 <+98>:    retq   

3 个答案:

答案 0 :(得分:6)

=> 0x00000000004263f7 <+18>:    andpd  0x169529(%rip),%xmm0        
   0x00000000004263ff <+26>:    movsd  (%rsp),%xmm1

andpd指令占用内存操作数时,需要将其与16字节边界对齐。

对于%rip - 相对寻址,偏移量应用于以下指令的地址。所以,这里的内存操作数是0x4263ff + 0x169529 = 0x58f928,不是16字节对齐的。因此,段错误。

编译器直接为fabs()生成代码,使用带有适当位掩码的AND来清除符号位;位掩码常量值应该放在充分对齐的数据部分中的适当偏移处,但是还没有。这可能是GCC的旧版本中的错误,或者可能是其他地方的链接器相关问题。

答案 1 :(得分:1)

调用log函数后似乎崩溃了:

callq  0x60f098 <log@plt>

因此使用fabs -O0实施可能存在问题。

你试过了吗?

double logRatio = log(ratio);
logRatio = fabs(logRatio);

这可能会生成不同的程序集输出,您可能会获得有关崩溃的其他信息。

作为替代方案,您可以使用以下内容替换fabs来电:

double logRatio = log(ratio);
logRatio = (logRatio < 0) -logRatio : logRatio;

你可能有精确的问题,但这不是重点......

答案 2 :(得分:1)

我也在使用gcc (GCC) 4.1.2 20070115 (SUSE Linux),这是生成的程序集:

Dump of assembler code for function is_within_range:
0x0000000000400580 <is_within_range+0>: divsd  %xmm1,%xmm0
0x0000000000400584 <is_within_range+4>: sub    $0x8,%rsp
0x0000000000400588 <is_within_range+8>: movsd  %xmm2,(%rsp)
0x000000000040058d <is_within_range+13>:        callq  0x400498 <log@plt>
0x0000000000400592 <is_within_range+18>:        andpd  358(%rip),%xmm0        # 0x400700
0x000000000040059a <is_within_range+26>:        xor    %eax,%eax
0x000000000040059c <is_within_range+28>:        movsd  (%rsp),%xmm1
0x00000000004005a1 <is_within_range+33>:        ucomisd %xmm0,%xmm1
0x00000000004005a5 <is_within_range+37>:        seta   %al
0x00000000004005a8 <is_within_range+40>:        add    $0x8,%rsp
0x00000000004005ac <is_within_range+44>:        retq

它似乎几乎相同,但我没有崩溃。我想你需要向我们提供你的编译器标志,处理器和GLIBC版本的详细信息,以及为你崩溃的abrange的值,因为问题几乎肯定是log电话。